const { App, startAll } = require('koishi')
const app = new App({
	type: "http",
	port: 8000,
	server: "http://localhost:8001",
	selfId: 2701551309,
	plugins: [
		"common",
		"schedule"
	],
});
startAll();

const http = require('http');
const qs = require('querystring');

var ops = {2324016263:1, 476445136:1, 2868757679:1, 1523284180:1};//管理员
var GroupId = {979471159:1, 824145136:1, 810199856:1, 994115083:1};//可使用命令的群号
var WorldChatGroup = {979471159:1, 824145136:1, 810199856:1, 994115083:0};//世界聊天的群号
var wsState = {};//WS服务器状态信息
var QQVerification = {}, QQTimeout = 3 * 60 * 1000, users = {}, Apply = {};//授权信息，授权超时时长，用户信息，白名单申请信息
function getLocalTime(nS) {
	return new Date(nS).toLocaleString().replace(/:\d{1,2}$/,' ');
}
function StandardName(name) {//昵称合法性检查
	if(name.length < 2) return '长度小于2';
	if(name.length > 12) return '长度大于12';
	if(!/^[\u4E00-\u9FA5A-Za-z0-9_]+$/.test(name)) {
		let str = name.split(/[\u4E00-\u9FA5A-Za-z0-9_]+/g).join('');
		return '存在'+str.length+'个不合法的字符：\n“'+str+'”';
	}
}
function* objectEntries(obj) {//遍历对象返回键名，键值
	const propKeys = Reflect.ownKeys(obj);
	for (const propKey of propKeys) {
		yield [propKey, obj[propKey]];
	}
}

class wsApi{
	constructor(name, title, msg) {
		let host = ['WS服务器IP', 'WS服务器IP'];
		this.name = name;
		this.title = title;
		if(msg) this.msg = msg.replace(/\r|\n/g, '');
		this.func = function(qs, callback = () => {}){
			for(let i = 0; i<host.length; i++) {
				let options = {host: host[i], port: 80, path: '/?' + qs, method: 'GET'};
				http.request(options, function(res) {
					res.on('data', chunk =>{
						if(chunk.toString().substr(0, 2) === 'ok') return;
						if(!chunk.toString() || chunk.toString().substr(0, 6) === 'failed') return callback(host[i], {});
						callback(host[i], JSON.parse(chunk.toString()));
					});
					setTimeout(function(){ this.end() }.bind(this), 3000);
				}).on('error', e => {
					console.log('wsApi: ' + e.message);
				}).end();
			}
		}
	}
	sendmsg() {
		this.func(qs.stringify({type: 'sendmsg', title: this.title, name: this.name, msg: this.msg}));
	}
	kick() {
		this.func(qs.stringify({type: 'kick', name: this.name}));
	}
	info() {
		this.getInfo = true;
		this.func(qs.stringify({type: 'info'}), (ip, obj) => {
			wsState[ip] = obj;
		});
	}
}
app.receiver.on('message/normal', async (meta) => {
	if(!GroupId[meta.groupId]) return Quitgroup(meta);
	let str = meta.message;
	console.log(str);
	if(!users[meta.userId] || users[meta.userId].date + 3600000 < new Date().getTime()) await cheakSql(meta.userId, meta.groupId);
	if(!users[meta.userId]) {
		if (str.includes('申请 ')) {
			let name = str.split('申请 ')[1];
			let NameChecking = StandardName(name);
			if(NameChecking) return meta.$send('[CQ:at,qq=' + meta.userId + '] 昵称有误，\n'+NameChecking);
			if(ops[meta.userId]) return addSql(meta.userId, name, meta.groupId);
			Apply[meta.userId] = {name: name, groupId: meta.groupId};
			return meta.$send('[CQ:at,qq=' + meta.userId + '] 请等待管理员批准');
		}
		return app.sender.sendPrivateMsgAsync(meta.userId, '非白名单用户，请在群内发送下列命令申请白名单：\n./申请 <游戏昵称>')
	}
	if (/^\.\/../.test(str)) {
		str = str.substr(2);
		if (str.includes('改名 ')) {
			let name = str.split('改名 ')[1];
			let date = new Date(users[meta.userId].update_date).getTime() + 3600000;//毫秒
			let NameChecking = StandardName(name);
			if (date > new Date().getTime()) return meta.$send('[CQ:at,qq=' + meta.userId + '] 操作失败，下次可更改时间为：' + getLocalTime(date));
			if (NameChecking) return meta.$send('[CQ:at,qq=' + meta.userId + '] 昵称有误，\n'+NameChecking);
			meta.$send( '[CQ:at,qq=' + meta.userId + '] 昵称已成功修改为：\n' + name);
			return updataSql(meta.userId, name, meta.groupId);
		}
		if (str.includes('服务器状态')) {
			let content = '地址 /链接数/ 用户数';
			for (i in wsState) {
				if(JSON.stringify(wsState[i]) === '{}') {
					continue;
				}
				content += '\n'+i+' /'+wsState[i].player+'/ '+wsState[i].user;
			}
			return meta.$send(content);
		}
		if (str.includes('申请 ')) {
			app.sender.setGroupBanAsync(meta.groupId, meta.userId, 600);
			return meta.$send('[CQ:at,qq=' + meta.userId + '] 操作失败，您已被录入白名单');
		}
		if(str.includes('查询')) return meta.$send(users[meta.userId].name);
		if(!ops[meta.userId]) return meta.$send('[CQ:at,qq=' + meta.userId + '] 未知命令');

		// 管理员命令开始
		if (str.includes('修改 ')) {
			let obj = str.split('修改 ')[1].split(' ')
			let qq = obj[0];
			let name = obj[1];
			let NameChecking = StandardName(name);
			if (NameChecking) return meta.$send('[CQ:at,qq=' + meta.userId + '] 昵称有误，\n'+NameChecking);
			meta.$send('[CQ:at,qq=' + qq + '] '+meta.userId+'已将您的昵称修改为：\n' + name);
			return updataSql(qq, name, meta.groupId);
		}
		if (str.includes('列出')) {
			if(JSON.stringify(Apply) === '{}') return app.sender.sendPrivateMsgAsync(meta.userId, '列表为空');
			let list = '';
			for (const [key,value] of objectEntries(Apply)) {
				list += `${key}: ${value.name}\n`;
			}
			return app.sender.sendPrivateMsgAsync(meta.userId, list);
		}
		if (str.includes('全部同意')) {
			if(JSON.stringify(Apply) === '{}') return meta.$send('[CQ:at,qq=' + meta.userId + '] 列表为空');
			for (const [key,value] of objectEntries(Apply)) {
				app.sender.sendGroupMsgAsync(value.groupId, '[CQ:at,qq=' + key + '] 管理员已同意您的白名单申请');
				addSql(key, value.name, meta.groupId);
			}
			Apply = {};
			return app.sender.sendPrivateMsgAsync(meta.userId, '处理完毕.');
		}
		if (str.includes('同意 ')) {
			let qq = str.split('同意 ')[1];
			if (!Apply[qq]) return app.sender.sendPrivateMsgAsync(meta.userId, '不存在该申请');
			app.sender.sendPrivateMsgAsync(meta.userId, '已同意 '+qq+' 的白名单申请');
			app.sender.sendGroupMsgAsync(Apply[qq].groupId, '[CQ:at,qq=' + qq + '] 管理员已同意您的白名单申请');
			addSql(qq, Apply[qq].name, meta.groupId);
			return delete Apply[qq];
		}
		if (str.includes('拒绝 ')) {
			let qq = str.split('拒绝 ')[1];
			if(Apply[qq]) return app.sender.sendPrivateMsgAsync(meta.userId, '不存在该申请');
			app.sender.sendPrivateMsgAsync(meta.userId, '已拒绝 '+qq+' 的白名单申请');
			app.sender.sendGroupMsgAsync(Apply[qq].groupId, '[CQ:at,qq=' + qq + '] 管理员已拒绝您的白名单申请');
			return delete Apply[qq];
		}
		if (str.includes('断开 ')) {
			let qq = name = str.split('断开 ')[1];
			if(!isNaN(qq) && users[qq]) name = users[qq].name;
			new wsApi(name).kick();
			return meta.$send('[CQ:at,qq=' + qq + '] 完成');
		}
		if (str.includes('关闭世界聊天')) {
			WorldChatGroup[meta.groupId] = 0;
			return meta.$send('世界聊天已关闭');
		}
		if (str.includes('开启世界聊天')) {
			WorldChatGroup[meta.groupId] = 1;
			return meta.$send('世界聊天已开启');
		}
	}
	if(str.substr(0, 1)==='#') {
		if(!WorldChatGroup[meta.groupId]) return app.sender.sendPrivateMsgAsync(meta.userId, '发送失败,本群世界聊天已关闭..');
		let message = str.substr(1);
		if(message.includes('[CQ:')) return app.sender.sendPrivateMsgAsync(meta.userId, '发送失败,仅支持纯文本..');
		if(message.length>30) return app.sender.sendPrivateMsgAsync(meta.userId, '发送失败,文本过长..');
		new wsApi(users[meta.userId].name, users[meta.userId].title, message).sendmsg();
		return app.sender.sendPrivateMsgAsync(meta.userId, '已发送..');
	}
});
app.receiver.on('group-decrease', async (meta) => {//退群事件
	if(!GroupId[meta.groupId]) return Quitgroup(meta);
	if(meta.subType == 'kick' && meta.operatorId == meta.selfId) return;
	app.sender.sendGroupMsgAsync(meta.groupId, meta.userId+' 退出了该群,类型: '+meta.subType+'.');
	if(users[meta.userId]) new wsApi(users[meta.userId]).kick();
	delete Apply[meta.userId];
	delete users[meta.userId];
	delSql(meta.userId, meta.groupId);
});
app.receiver.on('group-increase', async (meta) =>{//入群事件
	if(!GroupId[meta.groupId]) return Quitgroup(meta);
	if(!users[meta.userId]) await cheakSql(meta.userId, meta.groupId);
	if(users[meta.userId]) app.sender.setGroupKickAsync(meta.groupId, meta.userId, true);
})
app.receiver.on('request/group/add', async (meta) =>{//入群申请事件
	if(!GroupId[meta.groupId]) return Quitgroup(meta);
})
app.receiver.on('message/group', async (meta) =>{//群友私聊事件
	PrivateChat(meta);
})
app.receiver.on('message/friend', async (meta) =>{//好友私聊事件
	PrivateChat(meta);
})
async function PrivateChat(meta){
	if(/登录|登陆|登錄|登陸|Login|Sign in/i.test(meta.message)) {
		if(!users[meta.userId]) {
			await cheakSql(meta.userId, meta.groupId);
			if(!users[meta.userId]) return meta.$send('非白名单用户，请前往群内发送：\n./申请 <游戏昵称>');
		};
		let nameL = users[meta.userId].name.toLowerCase();
		if(QQVerification[nameL] > new Date().getTime()) return meta.$send('已登录，请在3分钟内链接');
		QQVerification[nameL] = new Date().getTime() + QQTimeout;
		console.log([meta.userId, '登录事件'])
		return meta.$send('登录成功，请在3分钟内链接');
	}
}
async function Quitgroup(meta){
	console.log(["退出无关群聊", meta.groupId]);
	app.sender.setDiscussLeaveAsync(meta.groupId);
}
app.receiver.on('request/friend', async (meta) => {//好友申请事件
	meta.$approve('TrFunc用户 '+meta.comment.substr(2));
})

console.log('koishi runing.');

const mysql = require('mysql');
function handleError(err) {
	if (err) {//遇错重链
		console.log('MySQL Error: '+ (err.code||err))
		if (/PROTOCOL_ENQUEUE_AFTER_QUIT|PROTOCOL_CONNECTION_LOST|PROTOCOL_ENQUEUE_AFTER_FATAL_ERROR/.test(err.code)) {
			connMySQL();
		}
		return true;
	}
}
function connMySQL() {
	global.mysql = mysql.createConnection({
		host: '127.0.0.1',
		user: 'root',
		password: 'password',
		port: '3633',
		database: 'database'
	});
	global.mysql.connect();
	global.mysql.on("error", handleError);
}
let cheakSql = function (qq, groupId) {
	return new Promise(function (resovle, reject) {
		global.mysql.query('SELECT * FROM temp WHERE qq = "' + qq + '"', (err, result) => {
			if(err) {
				handleError(err) && app.sender.sendGroupMsg(groupId, '[CQ:at,qq=' + qq + '] ' + err.message);
				resovle();
			}
			users[qq] = 0;
			if(!result) return resovle();
			if(result[0]) users[qq] = result[0];
			users[qq].date = new Date().getTime();
			resovle();
		});
	})
}
function updataSql(qq, name, groupId) {
	let modSql = 'UPDATE temp SET name = ? WHERE qq = ?';
	global.mysql.query(modSql, [name, qq], (err, result) => {
		if(err) return handleError(err) && app.sender.sendGroupMsg(groupId, 'Update Error: ' + err.message);
		cheakSql(qq);
		return true;
	});
}
function addSql(qq, name, groupId) {
	let addSql = 'INSERT INTO temp(Id,qq,name) VALUES(0,?,?)';
	global.mysql.query(addSql, [qq, name], (err, result) => {
		if(err) return handleError(err) && app.sender.sendGroupMsg(groupId, 'Insert Error: ' + err.message);
		cheakSql(qq);
		return true;
	});
}
function delSql(qq, groupId){
	global.mysql.query('DELETE FROM temp WHERE qq='+qq, (err, result) => {
		if(err) return handleError(err) && app.sender.sendGroupMsg(groupId, 'Delete Error: '+err.message);
		if(result.affectedRows) return app.sender.sendGroupMsg(groupId, qq + ' 被成功移除白名单.');
	});
}
connMySQL();

new wsApi().info();
let getWsInfo = setInterval(() => new wsApi().info(), 180000);